use crate::*;

pub fn get_mask_and_value(data: String, bit_offset: i32, bit_width: i32) -> Option<(u32, u32)> {
    let mask = if bit_width < 32 {
        (1_u32 << bit_width) - 1
    } else {
        0xFFFFFFFF_u32
    };
    let mask = !mask << bit_offset;
    let value = if data == "set" {
        1_u32 << bit_offset
    } else if data == "clear" {
        0_u32
    } else {
        if let Some(num) = utils::parse_number_str(&data) {
            num as u32
        } else {
            return None;
        }
    };

    return Some((mask, value));
}

pub fn parse_svd_file() -> Option<(Device, String)> {
    let dialog = rfd::FileDialog::new()
        .set_title("Select SVD file")
        .add_filter("SVD File", &["svd"])
        .add_filter("ALL File", &["*"]);

    if let Some(path) = dialog.pick_file() {
        let xml = &mut String::new();
        let path_str = path.to_str().unwrap_or_default().to_string();
        if let Ok(mut f) = File::open(path) {
            if let Ok(size) = f.read_to_string(xml) {
                if size > 0 {
                    let cfg = Config::default().expand(true);
                    if let Ok(svd) = svd_parser::parse_with_config(xml, &cfg) {
                        return Some((svd, path_str));
                    }
                }
            }
        }
    }

    return None;
}

pub fn peripheral_get_data(ui_io: UiIo, mut core: Core, p_name: String, r_name: String) {
    let peripherals = ui_io.get_peripherals();
    for p_row in 0..peripherals.row_count() {
        if let Some(mut peripheral) = peripherals.row_data(p_row) {
            if peripheral.name == p_name {
                let base_addr = peripheral.base_addr as u32;

                let registers = peripheral.registers;
                for r_row in 0..registers.row_count() {
                    if let Some(mut register) = registers.row_data(r_row) {
                        let addr_offset = register.addr_offset as u32;
                        let mut data: [u32; 1] = [0; 1];
                        if let Ok(_) = core.read_32((base_addr + addr_offset) as u64, &mut data) {
                            register.value = format!("0x{:08X}", data[0]).into();

                            if register.name == r_name {
                                let fields = register.fields;
                                for f_row in 0..fields.row_count() {
                                    if let Some(mut field) = fields.row_data(f_row) {
                                        if field.bit_width < 32 {
                                            let mask = (1_u32 << field.bit_width) - 1;
                                            let value = (data[0] >> field.bit_offset) & mask;
                                            if field.bit_width == 1 {
                                                field.value =
                                                    if value > 0 { "1".into() } else { "0".into() };
                                            } else {
                                                field.value = format!("0x{:02X}", value).into();
                                            }
                                        } else {
                                            field.value = format!("0x{:08X}", data[0]).into();
                                        }

                                        fields.set_row_data(f_row, field);
                                        //fields.model_tracker().track_row_data_changes(f_row);
                                    }
                                }
                                register.fields = fields;
                                ui_io.set_help_tip(
                                    format!(
                                        "{}.{}= 0x{:08X}",
                                        peripheral.name, register.name, data[0]
                                    )
                                    .into(),
                                );

                                //registers.model_tracker().track_row_data_changes(r_row);
                                break;
                            }
                            registers.set_row_data(r_row, register);
                        } else {
                            ui_io.set_help_tip(
                                format!("{}.{} read FAIL", peripheral.name, register.name).into(),
                            );
                        }
                    }
                }
                peripheral.registers = registers;
                peripherals.set_row_data(p_row, peripheral);
                //peripherals.model_tracker().track_row_data_changes(p_row);
                if r_name != "" {
                    break;
                }
            }
        }
    }
}

pub fn peripheral_modify_data(
    ui_io: UiIo,
    mut core: Core,
    p_name: String,
    r_name: String,
    mask: u32,
    value: u32,
) {
    let peripherals = ui_io.get_peripherals();
    for p_row in 0..peripherals.row_count() {
        if let Some(peripheral) = peripherals.row_data(p_row) {
            if peripheral.name == p_name.to_string() {
                let base_addr = peripheral.base_addr as u32;
                let registers = peripheral.registers;
                for r_row in 0..registers.row_count() {
                    if let Some(mut register) = registers.row_data(r_row) {
                        if register.name == r_name.to_string() {
                            let addr_offset = register.addr_offset as u32;
                            let mut data: [u32; 1] = [0; 1];
                            if let Ok(_) = core.read_32((base_addr + addr_offset) as u64, &mut data)
                            {
                                data[0] = (data[0] & mask) | value;
                                if let Ok(_) =
                                    core.write_32((base_addr + addr_offset) as u64, &mut data)
                                {
                                    //println!("Addr:0x{:08X}=0x{:08X}", (base_addr + addr_offset), data[0]);
                                    //println!("mask:0x{:08X} value:0x{:08X}", mask, value);

                                    register.value = format!("0x{:08X}", data[0]).into();
                                    ui_io.set_help_tip(
                                        format!(
                                            "{}.{}= 0x{:08X}",
                                            peripheral.name, register.name, data[0]
                                        )
                                        .into(),
                                    );
                                } else {
                                    ui_io.set_help_tip(
                                        format!("{}.{} write FAIL", peripheral.name, register.name)
                                            .into(),
                                    );
                                }
                            } else {
                                ui_io.set_help_tip(
                                    format!("{}.{} read FAIL", peripheral.name, register.name)
                                        .into(),
                                );
                            }
                            break;
                        }
                    }
                }
                break;
            }
        }
    }
}

pub fn peripherals_generate(device: Device) -> Vec<Peripheral> {
    let mut peripherals: Vec<Peripheral> = Vec::new();
    for m_pi in device.peripherals {
        match m_pi {
            MaybeArray::Single(pi) => {
                let mut p_regs: Vec<PeripheralReg> = Vec::new();
                for m_ri in pi.registers() {
                    match m_ri {
                        MaybeArray::Single(ri) => {
                            let mut p_fields: Vec<PeripheralField> = Vec::new();

                            for m_fi in ri.fields() {
                                match m_fi {
                                    MaybeArray::Single(fi) => {
                                        let field = PeripheralField {
                                            name: fi.name().into(),
                                            value: "-".into(),
                                            bit_offset: fi.bit_offset() as i32,
                                            bit_width: fi.bit_width() as i32,
                                        };
                                        p_fields.push(field);
                                    }
                                    MaybeArray::Array(fi, d) => {
                                        println!("{:?} {:?}", fi.name, d.dim_name);
                                    }
                                }
                            }

                            let reg = PeripheralReg {
                                name: ri.name().into(),
                                value: "-".into(),
                                addr_offset: ri.address_offset as i32,
                                fields: Rc::new(VecModel::from(p_fields)).into(),
                            };
                            p_regs.push(reg);
                        }
                        MaybeArray::Array(ri, d) => {
                            println!("{:?} {:?}", ri.name, d.dim_name);
                        }
                    }
                }

                let p = Peripheral {
                    base_addr: pi.base_address as i32,
                    name: pi.name().into(),
                    registers: Rc::new(VecModel::from(p_regs)).into(),
                };
                peripherals.push(p);

                // if let Some(desc) = pi.description {
                //     println!("{}", desc);
                // } else {
                //     println!(" -");
                // }
            }
            MaybeArray::Array(pi, d) => {
                println!("{:?} {:?}", pi.name, d.dim_name);
            }
        }
    }

    return peripherals;
}
